//
// Created by denglibin on 19-10-21.
//

/**
 * pause()函数
 * 调用该函数（系统调用）的进程将处于阻塞状态（主动放弃cpu），直到有信号递达将其唤醒。
int pause(void);    返回值：-1 并设置errno为EINTR  该函数只有一个返回值，可以理解为只有成功返回值，且为-1，
 同时errno的值置为EINTR。
 注意，只有当一个信号递达且处理方式被捕捉时，pause函数引起挂起操作的进程才会被唤醒，
 而且只有当信号处理完后（调用完用户处理函数），pause函数才返回-1，且errno置EINTR，进程被唤醒继续执行后面的程序。
 如果信号的处理方式为默认处理方式或者忽略（丢弃），那么pause函数不会返回值，且进程也不会被激活，而是一直阻塞（挂起）。
pause收到的信号不能被屏蔽，如果被屏蔽，那么pause就不能被唤醒
 */
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>

void sig_alrm(){

}

/**
 * 实现sleep函数 使用pause + alarm 原子操作 有时序问题
 * @param secs
 * @return
 */
static unsigned  int my_sleep(unsigned int secs){
    struct sigaction new_act, old_act;
    unsigned  int un_sleep;

    new_act.sa_handler = sig_alrm; // 设置信号捕捉函数
    sigemptyset(&new_act.sa_mask);
    new_act.sa_flags = 0;
    sigaction(SIGALRM, &new_act, &old_act); // 原来的备份到old
    alarm(secs); // 开始计时 时序问题： 极限条件下 执行到这里时进程失去cpu的时间很长，ALARM信号已被处理了才执行到pause，程序会一直阻塞下去。
    pause(); // 阻塞 直到捕捉到信号
    un_sleep = alarm(0); // 关闭闹钟
    sigaction(SIGALRM, &old_act, NULL); // 通过old还原
    return un_sleep;
}

/**
 * 使用 sigsuspend 函数 具有原子性 无时序问题
 * @param secs
 * @return
 */
static unsigned int my_sleep2(unsigned int secs){
    struct sigaction new_act, old_act;
    sigset_t new_mask, old_mask, sus_mask; // 信号屏蔽字
    unsigned int un_sleep;
    new_act.sa_handler = sig_alrm;
    sigemptyset(&new_act.sa_mask);
    new_act.sa_flags = 0;
    sigaction(SIGALRM, &new_act, &old_act); // 设置SIGALARM信号处理函数

    sigemptyset(&new_mask); // 清空
    sigaddset(&new_mask, SIGALRM); // 将SIGALRM 加入
    sigprocmask(SIG_BLOCK, &new_mask, &old_mask); // 阻塞 SIGALRM信号 （替换系统默认的信号屏蔽字，并把系统默认的备份到old_mask）

    alarm(secs); // 开始计时 时间到后发送SIGALRM信号
    sus_mask = old_mask;
    sigdelset(&sus_mask, SIGALRM); // 从集合中移除信号

    sigsuspend(&sus_mask);  // 原子操作 除了SIGALRM信号 其他信号都会阻塞掉，程序继续挂起（闹钟时间到了后发送SIGALRM信号，程序继续执行）
    // sigsuspend会阻塞信号，当遇到之外的信号，被signal注册的函数处理了，那么sigsuspend会正确返回

    un_sleep = alarm(0); // 取消闹钟
    sigaction(SIGALRM, &old_act, NULL); // 还原信号默认动作
    sigprocmask(SIG_SETMASK, &old_mask, NULL); // 还原信号屏蔽字 （取消对SIGALRM信号的阻塞）
    return un_sleep;
}

  static int main_(){
    printf("sleep start\n");
    //my_sleep(5);
    my_sleep2(3);
    printf("sleep end\n");
    return 0 ;
}
